package com.ruifu.act.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.fhzncloud.cloud.admin.api.feign.RemoteRoleService;
import com.fhzncloud.cloud.admin.api.feign.RemoteUserService;
import com.fhzncloud.cloud.common.core.constant.CommonConstants;
import com.fhzncloud.cloud.common.core.constant.SecurityConstants;
import com.fhzncloud.cloud.common.core.util.PageR;
import com.fhzncloud.cloud.common.core.util.R;
import com.fhzncloud.cloud.common.core.util.StringUtils;
import com.ruifu.act.service.ProcessActService;
import com.ruifu.act.utils.ActivityCommonUtil;
import com.ruifu.act.vo.AuditRecordVO;
import com.ruifu.common.vo.actanddoc.ActivitiVO;
import com.ruifu.common.vo.actanddoc.Flag;
import com.ruifu.common.vo.actanddoc.ProccessTaskVO;
import com.ruifu.common.vo.actanddoc.ProcessParams;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.bpmn.model.FlowElement;
import org.activiti.bpmn.model.UserTask;
import org.activiti.engine.*;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.impl.bpmn.behavior.MultiInstanceActivityBehavior;
import org.activiti.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.activiti.engine.impl.identity.Authentication;
import org.activiti.engine.impl.persistence.deploy.DeploymentCache;
import org.activiti.engine.impl.persistence.entity.HistoricProcessInstanceEntity;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.Execution;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Comment;
import org.activiti.engine.task.Task;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

/**
 * ClassName: ProcessActServiceImpl <br/>
 * Description: <br/>
 * date: 2020-5-7-0007 17:02<br/>
 *
 * @author liun<br />
 * @since JDK 1.8
 */
@Service
public class ProcessActServiceImpl implements ProcessActService {

    @Autowired
    private ProcessEngine processEngine;

    @Autowired
    private RepositoryService repositoryService;

    @Autowired
    private RuntimeService runtimeService;

    @Autowired
    private TaskService taskService;

    @Autowired
    private HistoryService historyService;

    @Autowired
    private RemoteRoleService remoteRoleService;

    @Autowired
    private RemoteUserService remoteUserService;

    /**
     * 提交(启动实例)
     *
     * @param processParams
     * @param tenantId
     */
    @Override
    public Flag submit(ProcessParams processParams, Integer tenantId) {

        Map<String, Object> variables = new HashMap<>();
        Flag flag = new Flag();
        flag.setIsSubmitSucceed(false);

        String businessKey = "business" + ":" + processParams.getDvDvId();//从外部传来的业务key.一个businessKey对应惟一的一个流程def-key
        String processDefKey = processParams.getDcWorkFlowId();

        List<Execution> executionParent = runtimeService.createExecutionQuery()
                .processInstanceBusinessKey(businessKey).list();

        //非空直接返回，防止对同一个文档重复提交操作，从而产生多个流程实例
        if (CollectionUtil.isNotEmpty(executionParent)) {
            return flag;
        }

        //设置发起人
        Authentication.setAuthenticatedUserId(processParams.getSubmitDocumentPerson());

        //获取最新版本的流程定义账套id
        List<ProcessDefinition> processDefinitionList = repositoryService
                .createProcessDefinitionQuery()
                .processDefinitionTenantId(tenantId.toString()) //该账套下的
                .processDefinitionKey(processDefKey)    //流程defkey对应的多个版本的流程部署定义
                .latestVersion()   //按照版本降序排序，查出最新的一个版本流程定义
                .list();

        if (CollectionUtil.isNotEmpty(processDefinitionList)) {

            ProcessDefinition pd = processDefinitionList.get(0);

            //尝试初始化map列表
            this.getInitVariableMap(variables, pd.getId(), null);

            //启动
            runtimeService.startProcessInstanceByKeyAndTenantId(processParams.getDcWorkFlowId(), businessKey, variables, tenantId.toString());

            flag.setIsSubmitSucceed(true);
        }

        return flag;
    }

    /**
     * 待审核
     *
     * @param userId   用户id
     * @param tenantId
     */
    @Override
    public PageR<List<ActivitiVO>> iReview(Integer userId, Integer page, Integer size, Integer tenantId) {
        page = (null == page) ? 1 : page;
        size = (null == size) ? 5 : size;

        List<ActivitiVO> activitiVOList = new ArrayList<>();

        long total = 0L;

        //1.查询我负责的任务列表（通过当前登录的用户id）

        List<Task> tasks = taskService
                .createTaskQuery()
                .taskAssignee(userId.toString())
                .taskTenantId(tenantId.toString()).list();

        //2.根据当前登录用户所在的角色，来查询对应分配的角色任务
        R userResult = remoteUserService.getByIds(CollectionUtil.newArrayList(new Integer[]{userId}), SecurityConstants.FROM_IN);
        List<Integer> roleList = new ArrayList<>();
        if (null != userResult) {
            List<LinkedHashMap> resultDataList = (List<LinkedHashMap>) userResult.getData();
            resultDataList.stream().forEach(map -> {
                List<LinkedHashMap> roleTmp = (List<LinkedHashMap>) map.get("roleList");
                roleTmp.stream().forEach(role -> {
                    roleList.add((Integer) role.get("roleId"));
                });
            });
        }

        for (Integer roleId : roleList) {
            tasks.addAll(taskService.createTaskQuery()
                    .taskAssigneeLike("%ROLE-" + roleId.toString() + "%")
                    .taskTenantId(tenantId.toString())
                    .orderByTaskCreateTime().desc()
                    .list());
        }

        //3.遍历所有任务task，进行前端对应的值拼接
        if (CollectionUtil.isNotEmpty(tasks)) {
            for (Task task : tasks) {
                ActivitiVO activitiVO = new ActivitiVO();

                //3.1 获取当前任务节点，上一步的连线message信息（Y：上一步为通过；N：上一步为驳回）
                String flowMassage = runtimeService.getVariable(task.getProcessInstanceId(), "message", String.class);
                if (StringUtils.isNotEmpty(flowMassage)) {
                    activitiVO.setFlowMassage(flowMassage);
                }

                //3.2 获取当前任务节点的相关信息
                activitiVO.setPresentLinkName(task.getName());
                activitiVO.setProcessInstanceId(task.getProcessInstanceId());
                activitiVO.setTaskId(task.getId());

                //3.3 获取历史任务节点的相关信息
                HistoricTaskInstance taskInstance = ActivityCommonUtil.queryUpOneNodeMessage(task.getProcessInstanceId(), historyService);
                if (taskInstance != null) {
                    //上一节点名称
                    activitiVO.setUpLinkName(taskInstance.getName());
                    //上一环节办理人
                    activitiVO.setUpTaskAss(taskInstance.getAssignee());
                }

                //3.4 获取业务主键
                ProcessInstance processInstance = runtimeService
                        .createProcessInstanceQuery()
                        .processInstanceId(task.getProcessInstanceId()).singleResult();
                String businessKey = processInstance.getBusinessKey();  //business:223-doc

                //判断业务主键是否为空且长度大于9
                if (businessKey != null && businessKey.length() > 9) {
                    //获取文档ID
                    String substring = businessKey.substring((businessKey.lastIndexOf(":") + 1));  //223-doc
                    activitiVO.setBusinessKey(substring);
                } else {
                    throw new RuntimeException("请查看业务主键是否为空！");
                }

                //3.5 添加任务最后分配时间
                activitiVO.setLastUpdateTime(task.getCreateTime());

                activitiVOList.add(activitiVO);
            }
        }

        total = activitiVOList.size();

        //按照时间降序排序
        activitiVOList.sort((list1, list2) -> list2.getLastUpdateTime().compareTo(list1.getLastUpdateTime()));

        //假分页操作
        // TODO 因为待审核这里查询数据分几个部分，并且又需要排序操作，所以只能采取假分页的形式，先查出所有，再截取片段。等后面有更好的解决方案再改
        int index = size * (page - 1) > total ? 0 : size * (page - 1);
        int end = (index + size) > total ? (int) (total - index) : size;

        return PageR.ok(activitiVOList.subList(index, end), total);
    }

    /**
     * 已审核
     *
     * @param userId
     * @param tenantId
     * @return
     */
    @Override
    public PageR<List<ActivitiVO>> queryDoneTasks(Integer userId, Integer page, Integer size, Integer tenantId) {
        page = (null == page || page.intValue() <= 0) ? 1 : page;
        size = (null == size || page.intValue() <= 0) ? 5 : size;

        List<ActivitiVO> activitiVOList = new ArrayList<>();

        //计算总数
        long total = historyService.createHistoricTaskInstanceQuery()
                .taskAssignee(userId.toString())
                .taskTenantId(tenantId.toString())
                .finished().count();

        /*根据审核人获取历史任务实例*/
        List<HistoricTaskInstance> tasks = historyService.createHistoricTaskInstanceQuery()
                .taskAssignee(userId.toString())
                .taskTenantId(tenantId.toString())
                .finished()
                .orderByHistoricTaskInstanceEndTime().desc()
                .listPage((page - 1) * size, size);

        if (tasks != null && tasks.size() > 0) {
            for (HistoricTaskInstance task : tasks) {
                ActivitiVO activitiVO = new ActivitiVO();

                /*获历史取流程实例*/
                HistoricProcessInstance historicProcessInstance = historyService
                        .createHistoricProcessInstanceQuery()
                        .processInstanceId(task.getProcessInstanceId()).singleResult();

                /*获取当前(活跃)任务节点信息*/
                List<Task> resultList = taskService
                        .createTaskQuery()
                        .processInstanceId(task.getProcessInstanceId())
                        .active().list();

                if (CollectionUtil.isNotEmpty(resultList)) {
                    //获取当前环节名称
                    activitiVO.setPresentLinkName(resultList.get(0).getName());
                } else {
                    activitiVO.setPresentLinkName("已结束");
                }

                //任务id
                activitiVO.setTaskId(task.getId());
                //流程实例Id
                activitiVO.setProcessInstanceId(task.getProcessInstanceId());
                //获取上一环节信息
                HistoricTaskInstance taskInstance = ActivityCommonUtil.queryUpOneNode(task.getId(), historyService);

                if (taskInstance != null) {
                    //上一节点名称
                    activitiVO.setUpLinkName(taskInstance.getName());
                    //上一环节办理人
                    activitiVO.setUpTaskAss(taskInstance.getAssignee());
                }

                //通过流程实例获取业务主键
                String businessKey = historicProcessInstance.getBusinessKey();

                //获取业务主键ID
                if (businessKey != null) {
                    String substring = businessKey.substring((businessKey.lastIndexOf(":") + 1));
                    //添加到文档id集合
                    activitiVO.setBusinessKey(substring);
                }

                //最后编辑时间
                activitiVO.setLastUpdateTime(task.getEndTime());

                activitiVOList.add(activitiVO);
            }
        }

        return PageR.ok(activitiVOList, total);
    }

    /**
     * 通过/驳回
     *
     * @param processParams
     */
    @Override
    public Flag pass(ProcessParams processParams) {
        Flag isFinish = new Flag();
        Map<String, Object> variables = new HashMap<String, Object>();

        //获取任务id
        String taskId = processParams.getTaskId();

        //通过任务id获取流程实例id

        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();

        if (null != task) {
            String processInstanceId = task.getProcessInstanceId();

            //通过任务id获取流程定义id
            String processDefinitionId = task.getProcessDefinitionId();

            //设置连线变量  Y  通过，N 驳回
            variables.put("message", processParams.getMark());

            //尝试将当前任务认领至本人
            taskService.setAssignee(taskId, null);//先将指派人赋空值
            taskService.claim(taskId, processParams.getAuditor());//再申领任务
            //taskService.setAssignee(taskId, processParams.getAuditor());

            //添加审核意见。设置评论提交人（当前审批人员）
            Authentication.setAuthenticatedUserId(processParams.getAuditor());

            if (StringUtils.isEmpty(processParams.getAuditOpinion())) {
                processParams.setAuditOpinion(" ");
            }

            taskService.addComment(
                    processParams.getTaskId(),
                    processInstanceId,
                    processParams.getAuditOpinion()
            );

            //初始化下一步可能存在的多实例列表
            this.getInitVariableMap(variables, processDefinitionId, processInstanceId);

            //完成任务
            taskService.complete(taskId, variables);
            isFinish.setIsSubmitSucceed(true);

            //通过流程实例id获取流程实例；判断流程是否结束
            ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
                    .processInstanceId(processInstanceId).singleResult();

            if (processInstance == null) {
                //结束
                isFinish.setIsFinish("yes");
            } else {
                isFinish.setIsFinish("no");
            }
        } else {
            //查询不到任务/任务被其他人申领，返回false
            isFinish.setIsSubmitSucceed(false);
        }

        return isFinish;
    }

    /**
     * 获取下一节点名称
     *
     * @param id   任务id
     * @param flag
     * @return
     */
    @Override
    public List<String> nextTaskName(String id, String flag) {
        // 创建任务查询
        Task task = taskService.createTaskQuery()
                .taskId(id)
                .singleResult();
        return ActivityCommonUtil.nextTaskName(task, flag, repositoryService);
    }

    /**
     * 获取当前正在执行节点的j节点信息
     *
     * @param businessKey 业务对象传过来的唯一流程标识
     * @return
     */
    @Override
    public ProccessTaskVO getNowTaskNode(String businessKey) {
        List<Integer> userIdList = new ArrayList<>();
        ProccessTaskVO proccessTask = new ProccessTaskVO();

        String splitBizKey = "";

        if (StringUtils.isNotEmpty(businessKey)) {
            splitBizKey = "business:" + businessKey;

            List<Execution> executionParentList = runtimeService.createExecutionQuery()
                    .processInstanceBusinessKey(splitBizKey)
                    .list();

            if (CollectionUtil.isNotEmpty(executionParentList)) {
                //获取当前最新的定义的执行流程
                Execution executionParent = executionParentList.get(0);

                List<Task> taskList = taskService.createTaskQuery().processInstanceId(executionParent.getId()).list();

                if (CollectionUtil.isNotEmpty(taskList)) {
                    List<Integer> userTmpList = new ArrayList<>();
                    List<Integer> roleList = new ArrayList<>();

                    String flowMassage = runtimeService.getVariable(executionParent.getId(), "message", String.class);
                    if (StringUtils.isNotEmpty(flowMassage)) {
                        proccessTask.setFlowMassage(flowMassage);
                    }

                    proccessTask.setProcessInstanceId(Integer.parseInt(executionParent.getProcessInstanceId()));
                    proccessTask.setTaskName(taskList.get(0).getName());

                    for (Task task : taskList) {
                        if (task.getAssignee().contains("ROLE")) {
                            //当前任务是角色 ROLE-223
                            for (String role : task.getAssignee().split(",")) {
                                role = role.split("-")[1];
                                roleList.add(Integer.parseInt(role));
                            }

                            //调用fein接口获取角色下的用户值
                            R userResult = remoteRoleService.getUserByRoleInner(roleList, SecurityConstants.FROM_IN);

                            if (null != userResult && CommonConstants.SUCCESS.equals(userResult.getCode())) {
                                //请求成功，并获取到数据
                                LinkedHashMap resultDataMap = (LinkedHashMap) userResult.getData();
                                List<LinkedHashMap> bbList;

                                //遍历获取map中每个role节点下的用户id信息
                                Iterator<Map.Entry<String, List<LinkedHashMap>>> entries = resultDataMap.entrySet().iterator();

                                while (entries.hasNext()) {
                                    Map.Entry<String, List<LinkedHashMap>> entry = entries.next();
                                    bbList = entry.getValue();

                                    for (LinkedHashMap map : bbList) {
                                        userTmpList.add((Integer) map.get("userId"));
                                    }
                                }
                            }

                        } else {
                            //当前任务是个人
                            userTmpList.add(Integer.parseInt(task.getAssignee()));
                        }
                    }

                    userIdList = userTmpList.stream().distinct().collect(Collectors.toList());
                }
            } else {
                //如果当前不存在正在执行的excution，则尝试查询历史process，看是否可以查询到
                List<HistoricProcessInstance> historyProcessList = historyService.createHistoricProcessInstanceQuery()
                        .processInstanceBusinessKey(splitBizKey)
                        .list();

                if (CollectionUtil.isNotEmpty(historyProcessList)) {
                    HistoricProcessInstanceEntity historyProcess = (HistoricProcessInstanceEntity) historyProcessList.get(0);
                    proccessTask.setProcessInstanceId(Integer.parseInt(historyProcess.getProcessInstanceId()));

                }
            }
        }
        proccessTask.setUserIdList(userIdList);

        return proccessTask;
    }

    /**
     * 查看流程审批全记录
     *
     * @param processInstanceId
     * @return
     */
    @Override
    public List<AuditRecordVO> AuditRecordList(String processInstanceId) {
        List<AuditRecordVO> auditRecordVoList = new ArrayList<>();
        List<Comment> comments;

        //查询历史记录
        List<HistoricActivityInstance> historyList = historyService
                .createHistoricActivityInstanceQuery()
                .processInstanceId(processInstanceId)
                .orderByHistoricActivityInstanceStartTime().desc()//按开始时间降序排列（让当前执行的节点位于最上面，流程开始节点放在最后）
                .list();

        //遍历历史记录
        String link = null;
        String executor = null;

        if (CollectionUtil.isNotEmpty(historyList)) {
            for (HistoricActivityInstance historyActivity : historyList) {
                AuditRecordVO auditRecordVO = new AuditRecordVO();

                //环节节点名称
                link = historyActivity.getActivityName();

                //环节节点审批人
                executor = historyActivity.getAssignee();

                //开始节点设置something
                if ("startEvent".equals(historyActivity.getActivityType())) {
                    //如果起始节点没有设置名称，则set一个默认值
                    if (StringUtils.isEmpty(link)) {
                        link = "流程开始";
                    }

                    //流程开始的执行人设置成流程发起人
                    executor = historyService.createHistoricProcessInstanceQuery()
                            .processInstanceId(processInstanceId).singleResult()
                            .getStartUserId();
                }

                //终止节点设置something
                if ("endEvent".equals(historyActivity.getActivityType())) {
                    //如果终止节点没有设置名称，则set一个默认值
                    link = "流程结束";
                }

                auditRecordVO.setLink(link);
                auditRecordVO.setExecutor(executor);
                auditRecordVO.setStarTime(historyActivity.getStartTime());
                auditRecordVO.setEndTime(historyActivity.getEndTime());

                //审核意见
                comments = taskService.getTaskComments((historyActivity.getTaskId()));
                if (CollectionUtil.isNotEmpty(comments)) {
                    auditRecordVO.setOpinion(comments.get(0).getFullMessage());
                } else {
                    auditRecordVO.setOpinion("");
                }
                auditRecordVoList.add(auditRecordVO);

            }
        }

        return auditRecordVoList;
    }

    private void getInitVariableMap(Map<String, Object> variables, String processDefinitionId, String processInstanceId) {
        //1.由于activiti缓存机制的存在。这里需要移除该实例的缓存，重新从数据库获取模版
        ProcessEngineConfigurationImpl processEngineConfiguration = (ProcessEngineConfigurationImpl) processEngine.getProcessEngineConfiguration();
        DeploymentCache deploymentCache = processEngineConfiguration.getDeploymentManager().getProcessDefinitionCache();
        deploymentCache.remove(processDefinitionId);


        //1.对默认发起者返回修改的参数进行赋值
        if (StringUtils.isNotEmpty(processInstanceId)) {
            String startUser = historyService.createHistoricProcessInstanceQuery()
                    .processInstanceId(processInstanceId).singleResult()
                    .getStartUserId();

            variables.put("roleSelf", startUser);
        }

        //2.对多任务的参数list进行赋值
        BpmnModel model = repositoryService.getBpmnModel(processDefinitionId);
        if (model != null) {
            Collection<FlowElement> flowElements = model.getMainProcess().getFlowElements();
            for (FlowElement flow : flowElements) {
                if (flow instanceof UserTask) {
                    UserTask userTask = (UserTask) flow;
                    Object behavior = userTask.getBehavior();

                    // 多实例用户任务需要将选择的roleId转换成用户id，并放到变量中
                    if (behavior instanceof MultiInstanceActivityBehavior) {

                        // 可以根据获取的候选组/或者指派人员ID列表查询用户列表，设定到多实例的变量
                        MultiInstanceActivityBehavior multiInstanceActivityBehavior = (MultiInstanceActivityBehavior) behavior;

                        String expressionStr = multiInstanceActivityBehavior.getCollectionExpression().getExpressionText(); //"${userListxxx}"
                        String roleId = "";
                        if (StringUtils.isNotBlank(expressionStr)) {
                            expressionStr = expressionStr.substring(2, expressionStr.length() - 1); //userListxxx
                        }

                        List<String> assigneeList = new ArrayList<>();
                        List<Integer> roleIds = new ArrayList<>();

                        String assignees = userTask.getAssignee();  //角色“ROLE-223,ROLE-224,ROLE-225”或者人员“333,444,555”
                        if (assignees.contains("ROLE")) {
                            for (String assignee : assignees.split(",")) {    //ROLE-223
                                roleId = assignee.split("-")[1];  //223
                                roleIds.add(Integer.parseInt(roleId));
                            }

                            //调用fein接口获取角色下的用户值
                            R userResult = remoteRoleService.getUserByRoleInner(roleIds, SecurityConstants.FROM_IN);

                            if (null != userResult && CommonConstants.SUCCESS.equals(userResult.getCode())) {
                                //请求成功，并获取到数据
                                LinkedHashMap resultDataMap = (LinkedHashMap) userResult.getData();
                                List<LinkedHashMap> bbList;

                                //遍历获取map中每个role节点下的用户id信息
                                Iterator<Map.Entry<String, List<LinkedHashMap>>> entries = resultDataMap.entrySet().iterator();
                                List<Integer> userIdsTemp = new ArrayList<>();

                                while (entries.hasNext()) {
                                    Map.Entry<String, List<LinkedHashMap>> entry = entries.next();
                                    bbList = entry.getValue();

                                    for (LinkedHashMap map : bbList) {
                                        userIdsTemp.add((Integer) map.get("userId"));
                                    }
                                }

                                //去重并转换成string类型
                                assigneeList = userIdsTemp.stream().distinct().map(String::valueOf).collect(Collectors.toList());
                            }
                        } else {
                            //如果存储的是用户id，直接存储就好了
                            for (String assignee : assignees.split(",")) {    //ROLE-223
                                assigneeList.add(assignee);
                            }
                        }

                        //将roleId对应成角色id，并以，分隔
                        userTask.setAssignee(StringUtils.join(assigneeList, ","));

                        variables.put(expressionStr, assigneeList);
                    }
                }
            }
        }
    }
}
